#include "GBoxOriented.h"

namespace GMath
{

	GBoxOriented::GBoxOriented()
	{
		Center = GVector3::Zero;
		Dimension = GVector3::One;

		Right = GVector3::UnitX;
		Up = GVector3::UnitY;
		Front = GVector3::UnitZ;
	}

	GBoxOriented::GBoxOriented(const GVector3& Center, const GVector3& Dimension, const GQuaternion& Rotation)
	{
		this->Center = Center;
		this->Dimension = Dimension;

		GQuaternion::Multiply(Right, GVector3::UnitX, Rotation);
		GQuaternion::Multiply(Up, GVector3::UnitY, Rotation);
		GQuaternion::Multiply(Front, GVector3::UnitZ, Rotation);
	}

	GBoxOriented::GBoxOriented(const GVector3& Center, const GVector3& Dimension, const GVector3& Front, const GVector3& Right, const GVector3& Up)
	{
		this->Center = Center;
		this->Dimension = Dimension;
	
		this->Right = Right;
		this->Up = Up;
		this->Front = Front;
	}

	void GBoxOriented::Create(const GVector3& Center, const GVector3& Dimension, const GQuaternion& Rotation)
	{
		this->Center = Center;
		this->Dimension = Dimension;

		GQuaternion::Multiply(Right, GVector3::UnitX, Rotation);
		GQuaternion::Multiply(Up, GVector3::UnitY, Rotation);
		GQuaternion::Multiply(Front, GVector3::UnitZ, Rotation);
	}

	void GBoxOriented::Create(const GVector3& Center, const GVector3& Dimension, const GVector3& Front, const GVector3& Right, const GVector3& Up)
	{
		this->Center = Center;
		this->Dimension = Dimension;
	
		this->Right = Right;
		this->Up = Up;
		this->Front = Front;
	}

	float GBoxOriented::Volume() const
	{
		return Dimension.x * Dimension.y * Dimension.z;
	}

	float GBoxOriented::SurfaceArea() const
	{
		return 2.0f * (Dimension.x * Dimension.y + 
						Dimension.x * Dimension.z + 
						Dimension.y * Dimension.z);
	}

	void GBoxOriented::GetNormal(GVector3& OutNormal, const GVector3& SurfacePoint) const
	{
		//!++ YAZILACAK
		OutNormal = GVector3::Zero;
	}

	void GBoxOriented::GetPlanes(OUTPUT GPlane PlaneArray[G_BP_COUNT]) const
	{
		GVector3 HalfDim = Dimension / 2.0f;

		GVector3 PointRight = Center + Right * HalfDim.x;
		GVector3 PointBottom = Center - Up * HalfDim.y;
		GVector3 PointLeft = Center - Right * HalfDim.x;
		GVector3 PointTop = Center + Up * HalfDim.y;
		GVector3 PointNear = Center - Front * HalfDim.z;
		GVector3 PointFar = Center + Front * HalfDim.z;

		PlaneArray[G_BP_RIGHT]	= GPlane(PointRight, -Right);
		PlaneArray[G_BP_BOTTOM] = GPlane(PointBottom, Up);
		PlaneArray[G_BP_LEFT]	= GPlane(PointLeft, Right);
		PlaneArray[G_BP_TOP]	= GPlane(PointTop, -Up);
		PlaneArray[G_BP_NEAR]	= GPlane(PointNear, Front);
		PlaneArray[G_BP_FAR]	= GPlane(PointFar, -Front);
	}

	void GBoxOriented::GetCorners(OUTPUT GVector3 Arr[G_BC_COUNT]) const
	{
		GVector3 WidthVec(Right * Dimension.x / 2.0f);
		GVector3 HeightVec(Up * Dimension.y / 2.0f);
		GVector3 DepthVec(Front * Dimension.z / 2.0f);

		Arr[G_BC_NEAR_BOTTOM_LEFT	] = Center - DepthVec - HeightVec - WidthVec;
		Arr[G_BC_NEAR_BOTTOM_RIGHT	] = Center - DepthVec - HeightVec + WidthVec;
		Arr[G_BC_NEAR_TOP_LEFT		] = Center - DepthVec + HeightVec - WidthVec;
		Arr[G_BC_NEAR_TOP_RIGHT		] = Center - DepthVec + HeightVec + WidthVec;
		Arr[G_BC_FAR_BOTTOM_LEFT	] = Center + DepthVec - HeightVec - WidthVec;
		Arr[G_BC_FAR_BOTTOM_RIGHT	] = Center + DepthVec - HeightVec + WidthVec;
		Arr[G_BC_FAR_TOP_LEFT		] = Center + DepthVec + HeightVec - WidthVec;
		Arr[G_BC_FAR_TOP_RIGHT	] = Center + DepthVec + HeightVec + WidthVec;
	}

	#define		G_BOX_X_AXIS		(GUInt32)0x01
	#define		G_BOX_Y_AXIS		(GUInt32)0x02
	#define		G_BOX_Z_AXIS		(GUInt32)0x04

	GVector3 GBoxOriented::GetCorner(GBoxCorner Corner) const
	{
		GVector3 HalfDim = Dimension / 2.0f;
		GVector3 HalfDimVec = Front * HalfDim.z + Up * HalfDim.y + Right * HalfDim.x;
	
		GVector3 Min = Center + HalfDimVec;
		GVector3 Max = Center - HalfDimVec;

		return GVector3(Corner & G_BOX_X_AXIS ? Max.x : Min.x, 
						Corner & G_BOX_Y_AXIS ? Max.y : Min.y, 
						Corner & G_BOX_Z_AXIS ? Max.z : Min.z);
	}

	const GVector3& GBoxOriented::GetRight() const
	{
		return Right;
	}

	const GVector3& GBoxOriented::GetUp() const
	{
		return Up;
	}

	const GVector3& GBoxOriented::GetFront() const
	{
		return Front;
	}

	const GBoxOriented& GBoxOriented::operator = (const GBoxOriented& Other)
	{
		Center = Other.Center;
		Dimension = Other.Dimension;
		Right = Other.Right;
		Up = Other.Up;
		Front = Other.Front;

		return *this;
	}
}